using System;
using System.Collections.Generic;
using System.IO.Ports;
using System.Linq;
using System.Net.Sockets;
using System.Reflection;
using System.Threading;
using HslCommunication;
using HslCommunication.Core;
using HslCommunication.ModBus;
using HslCommunication.Profinet.Siemens;
using Newtonsoft.Json;
using Opc.Ua;
using OpcUaHelper;
using WIT.DAQ.Core.Enums;
using WIT.DAQ.Core.Forms;
using WIT.DAQ.Core.Models;
using WIT.DAQ.Core.Protocols.Socket;

namespace WIT.DAQ.Core.Helper;

/// <summary>
/// Remoting帮助类
/// </summary>
public class RemotingHelper : MarshalByRefObject
{
	/// <summary>
	/// 远程连接的名称
	/// </summary>
	public const string RemotingName = "WITRemoting";

	/// <summary>
	/// 通过Socket连接发送消息后，接收到的消息
	/// </summary>
	private byte[] RECEIVESOCKETDATA = null;

	/// <summary>
	/// 测试方法
	/// </summary>
	/// <param name="content"></param>
	/// <returns></returns>
	public string Test(string content)
	{
		return "return content: " + content;
	}

	/// <summary>
	/// ReadValues
	/// </summary> 
	/// <param name="deviceId">设备</param> 
	/// <param name="daqItems">采集项</param>
	/// <param name="values">读取到的值</param>
	/// <param name="errMsg">错误信息</param> 
	/// <param name="structInsBuffer">动态类实例的集合，可以减少循环读取OPCUA结构体的性能消耗</param>
	/// <returns></returns>
	public bool ReadValues(Guid deviceId, List<DAQItem> daqItems, out List<object> values, out string errMsg, List<object> structInsBuffer = null)
	{
		bool flag = false;
		values = new List<object>();
		errMsg = string.Empty;
		try
		{
			Device device = Common.GetDevice(deviceId);
			if (device == null)
			{
				throw new Exception($"获取设备信息[{deviceId}]失败");
			}
			if (device.Protocol == EnumProtocol.OPCUA)
			{
				if (daqItems.Any((DAQItem t) => string.IsNullOrEmpty(t.NodeId)))
				{
					throw new Exception($"设备[{device.Name}]的通讯协议为[{device.Protocol}]，需要配置NodeId。");
				}
			}
			else
			{
				if (device.Protocol == EnumProtocol.Socket)
				{
					MethodBase currentMethod = MethodBase.GetCurrentMethod();
					throw new Exception($"设备[{device.Name}]的通讯协议为[{device.Protocol}]，不适用{currentMethod.Name}方法。");
				}
				if (daqItems.Any((DAQItem t) => string.IsNullOrEmpty(t.Address)))
				{
					throw new Exception($"设备[{device.Name}]的通讯协议为[{device.Protocol}]，需要配置Address。");
				}
			}
			object connection = GetConnection(device);
			flag = Common.ReadValues(connection, device, daqItems, out values, out errMsg, structInsBuffer);
			for (int i = 0; i < values.Count; i++)
			{
				if (!values[i].GetType().IsSerializable)
				{
					values[i] = JsonConvert.SerializeObject(values[i]);
				}
			}
		}
		catch (Exception ex)
		{
			flag = false;
			errMsg = ex.Message;
		}
		return flag;
	}

	/// <summary>
	/// WriteValues
	/// </summary> 
	/// <param name="deviceId">设备</param>
	/// <param name="daqItems">采集项</param>
	/// <param name="values">写入的内容</param>
	/// <param name="errMsg">错误信息</param>
	/// <returns></returns>
	public bool WriteValues(Guid deviceId, List<DAQItem> daqItems, List<object> values, out string errMsg)
	{
		bool flag = false;
		errMsg = string.Empty;
		try
		{
			Device device = Common.GetDevice(deviceId);
			if (device == null)
			{
				throw new Exception($"获取设备信息[{deviceId}]失败");
			}
			if (device.Protocol == EnumProtocol.OPCUA)
			{
				if (daqItems.Any((DAQItem t) => string.IsNullOrEmpty(t.NodeId)))
				{
					throw new Exception($"设备[{device.Name}]的通讯协议为[{device.Protocol}]，需要配置NodeId。");
				}
			}
			else
			{
				if (device.Protocol == EnumProtocol.Socket)
				{
					MethodBase currentMethod = MethodBase.GetCurrentMethod();
					throw new Exception($"设备[{device.Name}]的通讯协议为[{device.Protocol}]，不适用{currentMethod.Name}方法。");
				}
				if (daqItems.Any((DAQItem t) => string.IsNullOrEmpty(t.Address)))
				{
					throw new Exception($"设备[{device.Name}]的通讯协议为[{device.Protocol}]，需要配置Address。");
				}
			}
			object connection = GetConnection(device);
			flag = Common.WriteValues(connection, device, daqItems, values, out errMsg);
		}
		catch (Exception ex)
		{
			flag = false;
			errMsg = ex.Message;
		}
		return flag;
	}

	/// <summary>
	/// SendData_ToSocketServer
	/// </summary> 
	/// <param name="deviceId">设备</param>
	/// <param name="data">需要发送的消息</param>
	/// <param name="errMsg">错误信息</param>
	/// <returns></returns>
	public bool SendData_ToSocketServer(Guid deviceId, byte[] data, out string errMsg)
	{
		byte[] receiveData;
		return SendData_ToSocketServer(deviceId, data, out errMsg, receive: false, out receiveData);
	}

	/// <summary>
	/// SendData_ToSocketServer
	/// </summary> 
	/// <param name="deviceId">设备</param>
	/// <param name="data">需要发送的消息</param>
	/// <param name="errMsg">错误信息</param>
	/// <param name="receive">是否接收服务端返回的消息</param>
	/// <param name="receiveData">接收到的服务端消息</param>
	/// <param name="receiveTimeout">等待服务端返回消息的时长（ms），不建议超过10秒</param>
	/// <returns></returns>
	public bool SendData_ToSocketServer(Guid deviceId, byte[] data, out string errMsg, bool receive, out byte[] receiveData, int receiveTimeout = 1000)
	{
		bool result = false;
		errMsg = string.Empty;
		receiveData = null;
		TCPClient tCPClient = null;
		try
		{
			Device device = Common.GetDevice(deviceId);
			if (device == null)
			{
				throw new Exception($"获取设备信息[{deviceId}]失败");
			}
			if (device.Protocol != 0)
			{
				MethodBase currentMethod = MethodBase.GetCurrentMethod();
				throw new Exception($"设备[{device.Name}]的通讯协议为[{device.Protocol}]，不适用{currentMethod.Name}方法。");
			}
			object connection = GetConnection(device);
			tCPClient = (TCPClient)connection;
			int num = tCPClient.NetWork.Client.Send(data);
			if (num == data.Length)
			{
				if (receive)
				{
					if (receiveTimeout < 0)
					{
						receiveTimeout = 1000;
					}
					int num2 = 100;
					while (receiveTimeout <= 0)
					{
						receiveTimeout -= num2;
						Thread.Sleep(num2);
					}
					receiveData = RECEIVESOCKETDATA;
				}
				result = true;
			}
			else
			{
				errMsg = $"发送失败：需要发送的字节数为{data.Length}，实际发送的字节数为{num}";
			}
		}
		catch (Exception ex)
		{
			result = false;
			errMsg = ex.Message;
		}
		finally
		{
			tCPClient?.DisConnect();
		}
		return result;
	}

	/// <summary>
	/// SendData_ToSocketClient
	/// </summary> 
	/// <param name="ip">SocketClient的Ip</param>
	/// <param name="port">SocketClient的Port</param>
	/// <param name="data">需要发送的消息</param>
	/// <param name="errMsg">错误信息</param>
	/// <returns></returns>
	public bool SendData_ToSocketClient(string ip, int port, byte[] data, out string errMsg)
	{
		byte[] receiveData;
		return SendData_ToSocketClient(ip, port, data, out errMsg, receive: false, out receiveData);
	}

	/// <summary>
	/// SendData_ToSocketClient
	/// </summary> 
	/// <param name="ip">SocketClient的Ip</param>
	/// <param name="port">SocketClient的Port</param>
	/// <param name="data">需要发送的消息</param>
	/// <param name="errMsg">错误信息</param>
	/// <param name="receive">是否接收客户端返回的消息</param>
	/// <param name="receiveData">接收到的客户端消息</param>
	/// <param name="receiveTimeout">等待客户端返回消息的时长（ms），不建议超过10秒</param>
	/// <returns></returns>
	public bool SendData_ToSocketClient(string ip, int port, byte[] data, out string errMsg, bool receive, out byte[] receiveData, int receiveTimeout = 1000)
	{
		bool result = false;
		errMsg = string.Empty;
		receiveData = null;
		try
		{
			if (!MainForm.IS_SOCKETSERVER_LISTENING)
			{
				throw new Exception("WIT.DAQ客户端尚未开启Socket服务");
			}
			TCPClient tCPClient = MainForm.SOCKETCLIENTS.FirstOrDefault((TCPClient t) => t.IP == ip && t.Port == port);
			if (tCPClient == null)
			{
				throw new Exception($"Socket客户端[{ip}:{port}]尚未连接至Socket服务");
			}
			int num = tCPClient.NetWork.Client.Send(data);
			if (num == data.Length)
			{
				if (receive)
				{
					if (receiveTimeout < 0)
					{
						receiveTimeout = 1000;
					}
					int num2 = 100;
					while (receiveTimeout <= 0)
					{
						receiveTimeout -= num2;
						Thread.Sleep(num2);
					}
					receiveData = RECEIVESOCKETDATA;
				}
				result = true;
			}
			else
			{
				errMsg = $"发送失败：需要发送的字节数为{data.Length}，实际发送的字节数为{num}";
			}
		}
		catch (Exception ex)
		{
			result = false;
			errMsg = ex.Message;
		}
		return result;
	}

	/// <summary>
	/// 获取设备连接
	/// </summary>
	/// <param name="device"></param>
	/// <returns></returns>
	public object GetConnection(Device device)
	{
		switch (device.Protocol)
		{
		case EnumProtocol.OPCUA:
		{
			OpcUaClient opcUaClient = null;
			if (MainForm.CONNECTION_POOL.Any((Connection t) => t.DeviceId == device.Id && t.ConnectionIns.GetType() == typeof(OpcUaClient)))
			{
				opcUaClient = (OpcUaClient)MainForm.CONNECTION_POOL.FirstOrDefault((Connection t) => t.DeviceId == device.Id && t.ConnectionIns.GetType() == typeof(OpcUaClient)).ConnectionIns;
			}
			else
			{
				opcUaClient = new OpcUaClient();
				MainForm.CONNECTION_POOL.Add(new Connection
				{
					DeviceId = device.Id,
					Protocol = EnumProtocol.OPCUA,
					ConnectionIns = opcUaClient
				});
				if (device.NeedAccount.HasValue && device.NeedAccount.Value)
				{
					opcUaClient.UserIdentity = new UserIdentity(device.Account, device.Pwd);
				}
			}
			if (!opcUaClient.Connected)
			{
				try
				{
					opcUaClient.ConnectServer(device.Url);
					if (!opcUaClient.Connected)
					{
						throw new Exception($"{device.Protocol}连接设备[{device.Name}]失败");
					}
				}
				catch (Exception ex)
				{
					throw ex;
				}
			}
			return opcUaClient;
		}
		case EnumProtocol.S7:
		{
			SiemensS7Net siemensS7Net = null;
			if (MainForm.CONNECTION_POOL.Any((Connection t) => t.DeviceId == device.Id && t.ConnectionIns.GetType() == typeof(SiemensS7Net)))
			{
				siemensS7Net = (SiemensS7Net)MainForm.CONNECTION_POOL.FirstOrDefault((Connection t) => t.DeviceId == device.Id && t.ConnectionIns.GetType() == typeof(SiemensS7Net)).ConnectionIns;
			}
			else
			{
				siemensS7Net = new SiemensS7Net((SiemensPLCS)device.Model)
				{
					IpAddress = device.IP,
					Port = device.Port,
					Rack = Convert.ToByte(device.Rack),
					Slot = Convert.ToByte(device.Slot)
				};
				MainForm.CONNECTION_POOL.Add(new Connection
				{
					DeviceId = device.Id,
					Protocol = EnumProtocol.S7,
					ConnectionIns = siemensS7Net
				});
				OperateResult operateResult = siemensS7Net.ConnectServer();
			}
			return siemensS7Net;
		}
		case EnumProtocol.ModbusTcp:
		{
			ModbusTcpNet modbusTcpNet = null;
			if (MainForm.CONNECTION_POOL.Any((Connection t) => t.DeviceId == device.Id && t.ConnectionIns.GetType() == typeof(ModbusTcpNet)))
			{
				modbusTcpNet = (ModbusTcpNet)MainForm.CONNECTION_POOL.FirstOrDefault((Connection t) => t.DeviceId == device.Id && t.ConnectionIns.GetType() == typeof(ModbusTcpNet)).ConnectionIns;
			}
			else
			{
				ModbusTcpNet modbusTcpNet2 = new ModbusTcpNet(device.IP, device.Port, Convert.ToByte(device.Station));
				modbusTcpNet2.AddressStartWithZero = device.AddressStartWithZero.Value;
				modbusTcpNet2.DataFormat = (DataFormat)Enum.Parse(typeof(DataFormat), device.DataFormat, ignoreCase: true);
				modbusTcpNet2.IsStringReverse = device.IsStringReverse.Value;
				modbusTcpNet = modbusTcpNet2;
				MainForm.CONNECTION_POOL.Add(new Connection
				{
					DeviceId = device.Id,
					Protocol = EnumProtocol.ModbusTcp,
					ConnectionIns = modbusTcpNet
				});
				OperateResult operateResult2 = modbusTcpNet.ConnectServer();
			}
			return modbusTcpNet;
		}
		case EnumProtocol.ModbusRtu:
		{
			ModbusRtu modbusRtu = null;
			if (MainForm.CONNECTION_POOL.Any((Connection t) => t.DeviceId == device.Id && t.ConnectionIns.GetType() == typeof(ModbusRtu)))
			{
				modbusRtu = (ModbusRtu)MainForm.CONNECTION_POOL.FirstOrDefault((Connection t) => t.DeviceId == device.Id && t.ConnectionIns.GetType() == typeof(ModbusRtu)).ConnectionIns;
			}
			else
			{
				ModbusRtu modbusRtu2 = new ModbusRtu(Convert.ToByte(device.Station));
				modbusRtu2.AddressStartWithZero = device.AddressStartWithZero.Value;
				modbusRtu2.DataFormat = (DataFormat)Enum.Parse(typeof(DataFormat), device.DataFormat, ignoreCase: true);
				modbusRtu2.IsStringReverse = device.IsStringReverse.Value;
				modbusRtu = modbusRtu2;
				modbusRtu.SerialPortInni(device.ComPort, Convert.ToInt32(device.BaudRate), Convert.ToInt32(device.DataBits), (StopBits)device.StopBits.Value, (Parity)device.Parity.Value);
				MainForm.CONNECTION_POOL.Add(new Connection
				{
					DeviceId = device.Id,
					Protocol = EnumProtocol.ModbusRtu,
					ConnectionIns = modbusRtu
				});
			}
			if (!modbusRtu.IsOpen())
			{
				modbusRtu.Open();
			}
			return modbusRtu;
		}
		case EnumProtocol.Socket:
		{
			TCPClient tCPClient = new TCPClient();
			tCPClient.NetWork.Connect(device.IP, device.Port);
			tCPClient.NetWork.GetStream().BeginRead(tCPClient.buffer, 0, tCPClient.buffer.Length, TCPServerCallBack, tCPClient);
			return tCPClient;
		}
		default:
			throw new Exception($"未知的通信协议[{device.Protocol}]");
		}
	}

	/// <summary>
	/// SocketServer回调函数
	/// </summary>
	/// <param name="ar"></param>
	private void TCPServerCallBack(IAsyncResult ar)
	{
		TCPClient tCPClient = (TCPClient)ar.AsyncState;
		if (!tCPClient.NetWork.Connected)
		{
			return;
		}
		try
		{
			NetworkStream stream = tCPClient.NetWork.GetStream();
			byte[] array = new byte[stream.EndRead(ar)];
			if (array.Length != 0)
			{
				Array.Copy(tCPClient.buffer, array, array.Length);
				RECEIVESOCKETDATA = array;
				stream.BeginRead(tCPClient.buffer, 0, tCPClient.buffer.Length, TCPServerCallBack, tCPClient);
			}
			else
			{
				tCPClient.DisConnect();
			}
		}
		catch
		{
			tCPClient.DisConnect();
		}
	}
}
